package baiqitun.stupro.subject.service.impl;

import baiqitun.stupro.common.exception.enums.support.StuproExceptionEnum;
import baiqitun.stupro.common.exception.enums.support.ExistedExceptionEnum;
import baiqitun.stupro.common.exception.enums.support.NotFoundExceptionEnum;
import baiqitun.stupro.common.exception.support.NotFoundException;
import baiqitun.stupro.common.service.support.MyServiceImpl;
import baiqitun.stupro.common.util.DateTimeUtil;
import baiqitun.stupro.common.workflow.enums.ProcessDefinitionKeyEnum;
import baiqitun.stupro.common.exception.support.ExistedException;
import baiqitun.stupro.common.exception.StuproException;
import baiqitun.stupro.common.workflow.util.RuntimeTaskUtil;
import baiqitun.stupro.common.workflow.util.TaskUtil;
import baiqitun.stupro.security.enums.SysRoleEnum;
import baiqitun.stupro.security.entity.SysDepart;
import baiqitun.stupro.security.entity.SysRole;
import baiqitun.stupro.security.entity.SysUser;
import baiqitun.stupro.security.service.SysDepartService;
import baiqitun.stupro.security.service.SysDepartUserService;
import baiqitun.stupro.security.service.SysUserRoleService;
import baiqitun.stupro.security.service.SysUserService;
import baiqitun.stupro.subject.entity.SysStudentInfo;
import baiqitun.stupro.subject.mapper.SysStudentInfoMapper;
import baiqitun.stupro.subject.service.SysStudentInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.lang.StringUtils;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

/**
 * <p>
 * 学生信息表 服务实现类
 * </p>
 *
 * @author kedaji
 * @since 2020-04-01
 */
@Service
public class SysStudentInfoServiceImpl extends MyServiceImpl<SysStudentInfoMapper, SysStudentInfo> implements SysStudentInfoService {
    @Autowired
    private SysDepartService sysDepartService;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private SysDepartUserService sysDepartUserService;
    @Autowired
    private TaskUtil taskUtil;
    @Autowired
    private RuntimeTaskUtil runtimeTaskUtil;
    @Autowired
    private SysUserService sysUserService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean save(@Validated() SysStudentInfo entity) {
        return super.save(entity);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateById(@Validated() SysStudentInfo entity) {
        return super.updateById(entity);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public SysStudentInfo getStudentInfoByUserId(String userId) {
        SysStudentInfo queryStudentInfo = new SysStudentInfo();
        queryStudentInfo.setUserId(userId);
        return getOne(new QueryWrapper<>(queryStudentInfo.setUserId(userId)));
    }

    /**
     * 学生角色拥有以下角色权限
     * ROLE_ANONYMOUS(匿名)
     * ROLE_STUDENT(学生)
     * @param user 用户信息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveToDepart(@Validated() SysUser user,
                             @Validated() SysStudentInfo studentInfo, String departId) {
        SysDepart depart = sysDepartService.getExistedEntity(departId,(NotFoundException)
                new NotFoundException(NotFoundExceptionEnum.DEPART_NOT_FOUND).put(SysDepart.class, "id", departId));
        //学生只能保存到子部门（班级），不能保存到顶级部门（系）
        if (depart.getParentId() == null){
            throw new StuproException(StuproExceptionEnum.ILLEGAL_PARAMETER);
        }
        sysDepartUserService.saveUserToDepart(user, departId);
        studentInfo.setUserId(user.getId());
        sysUserRoleService.grantRoleForUser(user.getId(), SysRoleEnum.ROLE_ANONYMOUS.name(), user.getCreateBy());
        sysUserRoleService.grantRoleForUser(user.getId(), SysRoleEnum.ROLE_STUDENT.name(),  user.getCreateBy());
        this.save(studentInfo);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void sendSelectTutorRequest(String studentUserId, String tutorId, String description) {
        // 校验数据合法性
        sysUserService.checkEnabled(studentUserId);
        sysUserService.checkEnabled(tutorId);
        SysStudentInfo queryStudentInfo = new SysStudentInfo();
        queryStudentInfo.setUserId(studentUserId);
        SysStudentInfo studentInfo = getOne(new QueryWrapper<>(queryStudentInfo));
        // 检查学生是否已选择导师
        if (StringUtils.isNotBlank(studentInfo.getTutorId())){
            throw new StuproException(StuproExceptionEnum.ILLEGAL_OPERATION)
                    .put(SysStudentInfo.class,"tutorId", studentInfo.getTutorId());
        }
        // 检查学生是否发送过【选择导师】的申请，但导师还未处理
        if (taskUtil.countTaskByOwner(ProcessDefinitionKeyEnum.SELECT_TUTOR.name(), studentUserId) > 0){
             throw new ExistedException(ExistedExceptionEnum.TASK_EXISTED);
        }
        // 开始流程
        runtimeTaskUtil.startProcessInstanceWithTask(ProcessDefinitionKeyEnum.SELECT_TUTOR.name(),
                null,
                studentUserId, tutorId,
                ProcessDefinitionKeyEnum.SELECT_TUTOR.name(), description);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void withinSelectTutorRequest(String taskId, String description) {
        runtimeTaskUtil.within(ProcessDefinitionKeyEnum.SELECT_TUTOR.name(), taskId, description);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void sendCancelSelectedTutorRequest(String studentUserId, String description) {
        sysUserService.checkEnabled(studentUserId);
        SysStudentInfo queryStudentInfo = new SysStudentInfo();
        queryStudentInfo.setUserId(studentUserId);
        SysStudentInfo studentInfo = getOne(new QueryWrapper<>(queryStudentInfo));
        if (StringUtils.isBlank(studentInfo.getTutorId())){
            throw new StuproException(StuproExceptionEnum.ILLEGAL_OPERATION).put(SysStudentInfo.class,"id", studentUserId);
        }
        if (taskUtil.countTaskByOwner(ProcessDefinitionKeyEnum.CANCEL_SELECTED_TUTOR.name(), studentUserId) > 0){
            throw new ExistedException(ExistedExceptionEnum.TASK_EXISTED).put("studentUserId",studentUserId);
        }

        runtimeTaskUtil.startProcessInstanceWithTask(ProcessDefinitionKeyEnum.CANCEL_SELECTED_TUTOR.name(),null,
                studentUserId, studentInfo.getTutorId(),
                ProcessDefinitionKeyEnum.CANCEL_SELECTED_TUTOR.name(),
                description);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<Task> listTaskForSelectTutorRequest(String studentUserId) {
        return taskUtil.listTaskByOwner(ProcessDefinitionKeyEnum.SELECT_TUTOR.name(), studentUserId);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<SysUser> listStudent(String tutorId, int year) {
        // 查询导师带领的某届学生
        SysStudentInfo sysStudentInfo = new SysStudentInfo();
        sysStudentInfo.setTutorId(tutorId);
        List<SysStudentInfo> studentInfos = list(new QueryWrapper<>(sysStudentInfo).between("create_datetime",
                DateTimeUtil.firstDateTimeOfYear(year), DateTimeUtil.lastDateTimeOfYear(year)));

        List<SysUser> students = new LinkedList<>();
        for (SysStudentInfo student: studentInfos){
            SysUser user = sysUserService.getById(student);
            students.add(user);
        }
        return students;
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public SysUser getUserOfStudentInfo(String studentInfoId) {
        SysStudentInfo studentInfo = getExistedEntity(studentInfoId, new NotFoundException(NotFoundExceptionEnum.STUDENT_INFO_NOT_FOUND));
        return sysUserService.getById(studentInfo.getUserId());
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<SysUser> listStudentForClassOfYear(String departId, Integer year){
        SysDepart depart = sysDepartService.getById(departId);
        if (depart == null){
            throw new NotFoundException(NotFoundExceptionEnum.DEPART_NOT_FOUND).put("departId", departId);
        }
        if (StringUtils.isBlank(depart.getParentId())){
            // depart是顶级部门（系）
            throw new StuproException(StuproExceptionEnum.ILLEGAL_PARAMETER);
        }
        List<SysUser> users = sysDepartUserService.listUserForDepartOfYear(departId, year);
        List<SysUser> students = new LinkedList<>();
        for (SysUser user: users){
            List<SysRole> roles = sysUserRoleService.listRoleByUserId(user.getId());
            for (SysRole role: roles){
                if (SysRoleEnum.ROLE_STUDENT.name().equals(role.getCode())){
                    break;
                }
            }
            students.add(user);
        }
        return students;
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public int minYear() {
        return baseMapper.minYear().getYear();
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public int maxYear() {
        return baseMapper.minYear().getYear();
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<Integer> years() {
        HashSet<Integer> years = new HashSet<>();
        for (LocalDateTime createDateTime: baseMapper.years()){
            years.add(createDateTime.getYear());
        }
        return new LinkedList<>(years);
    }
}
